package ch.ethz.fcl.metrobuzz.data.ltt.link;

import java.util.ArrayList;
import java.util.List;

import ch.ethz.fcl.metrobuzz.algorithm.list.ListOperation;
import ch.ethz.fcl.metrobuzz.data.ltt.LongTermTrajectory;
import ch.ethz.fcl.metrobuzz.data.ltt.TrajectoryGraph;

public class FrequentLinkGraph {
	private TrajectoryGraph trajGraph;
	private List<LongTermTrajectory> frequentTrajs;
	private List<LTTLink> frequentLTTLinks;
	private List<LTTLongLink> longLinks;

	public FrequentLinkGraph(TrajectoryGraph trajGraph,
			List<LongTermTrajectory> frequentTrajs,
			List<LTTLink> frequentLTTLinks) {
		this.trajGraph = trajGraph;
		this.frequentTrajs = frequentTrajs;
		this.frequentLTTLinks = frequentLTTLinks;
	}

	// connect links together so that they form a graph
	public void processFrequentLink() {
		List<LTTLink> connectedLinks = new ArrayList<LTTLink>();

		while (connectedLinks.size() != frequentLTTLinks.size()) {
			List<LTTLink> remainLinks = ListOperation.difference(
					frequentLTTLinks, connectedLinks);

			connectLinks(connectedLinks, remainLinks.get(0));
		}

		longLinks = new ArrayList<LTTLongLink>();
		connectedLinks = new ArrayList<LTTLink>();

		while (connectedLinks.size() != frequentLTTLinks.size()) {
			List<LTTLink> remainLinks = ListOperation.difference(
					frequentLTTLinks, connectedLinks);
			LTTLongLink longLink = new LTTLongLink(trajGraph);
			longLink.addLink(remainLinks.get(0));

			putToLongLink(connectedLinks, longLink, remainLinks.get(0));

			longLinks.add(longLink);
		}

		// if (trajGraph.DEBUG) {
		// for (int i = 0; i < longLinks.size(); i++)
		// System.out.println("Long link " + i + ": "
		// + longLinks.get(i).toString());
		// }

		List<LTTLink> initializedLinks = new ArrayList<LTTLink>();

		while (initializedLinks.size() != frequentLTTLinks.size()) {
			List<LTTLink> remainLinks = ListOperation.difference(
					frequentLTTLinks, initializedLinks);

			remainLinks.get(0).createNodeFlowVolumeMap(initializedLinks);
		}
	}

	private void connectLinks(List<LTTLink> connectedLinks, LTTLink currentLink) {
		if (!connectedLinks.contains(currentLink))
			connectedLinks.add(currentLink);

		for (int i = 0; i < frequentLTTLinks.size(); i++) {
			LTTLink anotherLink = frequentLTTLinks.get(i);
			if (!anotherLink.equals(currentLink)) {
				// if another link is pre-link of current link
				if (anotherLink.getTo().equals(currentLink.getFrom())
						&& !anotherLink.getFrom().equals(currentLink.getTo())
						&& (currentLink.getPreLinks() == null || !currentLink
								.getPreLinks().contains(anotherLink))) {
					LongTermTrajectory ltt = new LongTermTrajectory();
					ltt.addNode(anotherLink.getFrom());
					ltt.addNode(anotherLink.getTo());
					ltt.addNode(currentLink.getTo());

					// check if this link is really exist in the frequent ltt
					boolean exist = false;
					for (LongTermTrajectory frequentLTT : frequentTrajs)
						if (frequentLTT.contains(ltt)) {
							exist = true;
							break;
						}

					if (exist) {
						anotherLink.addPostLink(currentLink);
						currentLink.addPreLink(anotherLink);

						connectLinks(connectedLinks, anotherLink);
					}
				}

				// if another link is post-link of current link
				if (anotherLink.getFrom().equals(currentLink.getTo())
						&& !anotherLink.getTo().equals(currentLink.getFrom())
						&& (currentLink.getPostLinks() == null || !currentLink
								.getPostLinks().contains(anotherLink))) {
					LongTermTrajectory ltt = new LongTermTrajectory();
					ltt.addNode(currentLink.getFrom());
					ltt.addNode(currentLink.getTo());
					ltt.addNode(anotherLink.getTo());

					// check if this link is really exist in the frequent ltt
					boolean exist = false;
					for (LongTermTrajectory frequentLTT : frequentTrajs)
						if (frequentLTT.contains(ltt)) {
							exist = true;
							break;
						}

					if (exist) {
						currentLink.addPostLink(anotherLink);
						anotherLink.addPreLink(currentLink);

						connectLinks(connectedLinks, anotherLink);
					}
				}
			}
		}
	}

	private void putToLongLink(List<LTTLink> connectedLinks,
			LTTLongLink longLink, LTTLink currentLink) {
		connectedLinks.add(currentLink);
		currentLink.setLongLink(longLink);
		
		if (currentLink.getPreLinks() != null
				&& currentLink.getPreLinks().size() == 1) {
			LTTLink preLink = currentLink.getPreLinks().get(0);
			if (!longLink.getLinks().contains(preLink)
					&& (preLink.getPostLinks() == null || preLink
							.getPostLinks().size() == 1)) {
				longLink.addLinkInFront(currentLink.getPreLinks().get(0));
				putToLongLink(connectedLinks, longLink, currentLink
						.getPreLinks().get(0));
			}
		}

		if (currentLink.getPostLinks() != null
				&& currentLink.getPostLinks().size() == 1) {
			LTTLink postLink = currentLink.getPostLinks().get(0);
			if (!longLink.getLinks().contains(postLink)
					&& (postLink.getPreLinks() == null || postLink
							.getPreLinks().size() == 1)) {
				longLink.addLink(postLink);
				putToLongLink(connectedLinks, longLink, postLink);
			}
		}
	}

	public void render() {
		if (frequentLTTLinks != null)
			for (LTTLink link : frequentLTTLinks)
				link.render();

		// if (longLinks != null)
		// for (int i = 0; i < longLinks.size(); i++) {
		// ColorTable.assignColor(i, 1.0f);
		// Point2D[] pts = longLinks.get(i).getPassingPoints();
		// Point2D[] spline = CubicHermite.getCatmullRomSpline(100, pts);
		// MBGL.render(spline, GL11.GL_LINE_STRIP);
		// }
	}
}
